package com.zsl.swing.redis.desktop.window.cmd;

import com.zsl.swing.redis.desktop.common.Constants;
import com.zsl.swing.redis.desktop.common.IconPaths;
import com.zsl.swing.redis.desktop.model.CommandModel;
import com.zsl.swing.redis.desktop.model.FontSizeConfigEntity;
import com.zsl.swing.redis.desktop.model.NodeEntity;
import com.zsl.swing.redis.desktop.struct.FixedArrayList;
import com.zsl.swing.redis.desktop.type.BooleanEnum;
import com.zsl.swing.redis.desktop.type.CmdCommandEnum;
import com.zsl.swing.redis.desktop.type.OtherMenuEnum;
import com.zsl.swing.redis.desktop.utils.*;
import com.zsl.swing.redis.desktop.window.ZslRedisDesktopMainWindow;
import com.zsl.swing.redis.desktop.window.help.ZslRedisCmdHelpWindow;
import com.zsl.swing.redis.desktop.window.menu.ZslMenuItem;

import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

public class ZslRedisCmdWindows extends JFrame implements KeyListener, CaretListener {

    private final JTextArea console = new JTextArea();

    private final NodeEntity dbEntity;

    private String prefix;

    private StringBuffer buffer = new StringBuffer();

    private int currentPosition = 0;

    private int currentKeyCode;

//    private JFrame promptFrame ;

//    private JWindow promptWindow = new JWindow(this);

    private static final FixedArrayList<String> historyCommandList = ConfigUtils.readHistoryCommandList();

    private static final String F_FORMAT = "%s:%s[%s]>";

    private static final int WIDTH = Constants.MAIN_WINDOW_WIDTH*7/8;
    private static final int HEIGHT = Constants.MAIN_WINDOW_HEIGHT*7/8;

    private static int HISTORY_COMMAND_INDEX = historyCommandList.size() - 1;

    private ZslRedisCmdWindows(NodeEntity dbEntity){
        this.dbEntity = dbEntity;

        RedisUtils.putConnectionMap(this.dbEntity.getParent());
        this.prefix = this.buildPrefix(dbEntity);

        this.initCommandIndex();

        this.initConsole();

        this.initFrame();

//        this.initPromptFrame();
    }

//    private void initPromptFrame() {
//
//        this.promptFrame.setVisible(false);
//    }

    private void initCommandIndex(){
        HISTORY_COMMAND_INDEX = historyCommandList.size();
    }

    private void initFrame() {
        this.setTitle(this.dbEntity.getParent().getShowName());
        this.setIconImage(IconUtils.getScaleImage(IconPaths.REDIS_CONSOLE_ICON, Constants.ICON_WIDTH , Constants.ICON_HEIGHT));

        this.setSize(WIDTH, HEIGHT);

        FontSizeConfigEntity fontSizeConfig = ZslRedisDesktopMainWindow.getFontSizeConfig();
        this.setFont(FontUtils.getFont(fontSizeConfig.getFontName(),Font.BOLD, fontSizeConfig.getWindow()));


        int x = CommonUtils.maxWidth()/2 - WIDTH /2;
        int y = CommonUtils.maxHeight()/2 - HEIGHT /2;

        this.setLocation(x, y);
        this.setContentPane(new JScrollPane(this.console));

        this.setVisible(true);

        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                ConfigUtils.writeHistoryCommandList(historyCommandList);
            }
        });

    }

    private void initConsole() {
        this.console.setBackground(Color.DARK_GRAY);
        this.console.setForeground(Color.GREEN);
        this.console.setCaretColor(Color.GREEN);

        FontSizeConfigEntity fontSizeConfig = ZslRedisDesktopMainWindow.getFontSizeConfig();
        this.console.setFont(FontUtils.getFont(fontSizeConfig.getFontName(), Font.PLAIN, fontSizeConfig.getCmdArea()));
        this.console.setLineWrap(true);
        this.console.setEditable(true);

        this.console.addKeyListener(this);
        this.console.addCaretListener(this);

        this.console.addMouseListener(this.buildMouseAdapter());

        this.clear();
    }

    public MouseAdapter buildMouseAdapter(){
        return new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if(e.getButton() != MouseEvent.BUTTON3){
                    return;
                }

                String selectedText = ZslRedisCmdWindows.this.console.getSelectedText();
                if(!StringUtils.isEmpty(selectedText)){
                    JPopupMenu popupMenu = new JPopupMenu();
                    popupMenu.add(ZslMenuItem.getInstance(OtherMenuEnum.COPY.getText(), ZslRedisCmdWindows.this.buildActionListener()));
                    popupMenu.show(ZslRedisCmdWindows.this.console,e.getX(),e.getY());
                }
            }
        };

    }

    private ActionListener buildActionListener(){
        return event -> {
            String command = event.getActionCommand();
            if(OtherMenuEnum.COPY.getText().equals(command)){
                ZslRedisCmdWindows.this.console.copy();
            }
        };
    }

    private String buildPrefix(NodeEntity dbEntity){
        return String.format(F_FORMAT,dbEntity.getParent().getHost(),dbEntity.getParent().getPort(),dbEntity.getDbIndex());
    }


    @Override
    public void keyTyped(KeyEvent e) {
        this.initCommandIndex();
        if(this.currentPosition < this.buffer.length()){
            e.consume();
            return;
        }

        String text = this.console.getText();
        String input = text.replace(buffer.toString(),"");

        if(this.currentKeyCode != KeyEvent.VK_ENTER){
            /**
             * 自动提示
             */

            this.showPromptDialog(input);
        }else{
            /**
             * 执行命令
             */
            this.executeCommand(input);//执行命令
        }

    }

    private void showPromptDialog(String input) {

//        System.out.println("当前输入：" + input);
//
//        Vector<String> result = new Vector<>();
//        result.add(input);
//        if(result.isEmpty()){
//            return;
//        }
//        JList<String> list = new JList<>(result);
//        this.promptWindow.setContentPane(list);
//
//        this.promptWindow.setSize(800,400);
//
//        try {
//            Rectangle rectangle = this.console.modelToView(this.currentPosition);
//
//            System.out.println("当前坐标：" + rectangle);
//
//            this.promptWindow.setLocation(rectangle.x + rectangle.width, rectangle.y + rectangle.height);
//        } catch (BadLocationException e) {
//            e.printStackTrace();
//        }
//
//        this.promptWindow.setVisible(true);
//
//        this.console.requestFocus();
    }

    private void executeCommand(String input) {
        if(!input.endsWith("\n")){
            input = input.replace("\n","");
            input = input + "\n";
        }

        this.buffer.append(input);
        this.console.setText(this.buffer.toString());
        if(StringUtils.isEmpty(input)){
            this.append(this.prefix);
            return;
        }

        String target = input.trim();
        historyCommandList.add(target);
        this.initCommandIndex();
        if(CmdCommandEnum.CLEAR.getCommandName().equalsIgnoreCase(target)){
            this.clear();
        }else if(CmdCommandEnum.EXIT.getCommandName().equalsIgnoreCase(target)){
            this.dispose();
        }else if(CmdCommandEnum.HELP.getCommandName().equalsIgnoreCase(target)){
            this.append("\n");
            this.append(this.prefix);
            ZslRedisCmdHelpWindow.showHelpDialog();
        }else{
            StringTokenizer st = new StringTokenizer(target);
            String commandName = st.nextToken();
            List<String> params = new ArrayList<>();
            while(st.hasMoreElements()){
                params.add(st.nextToken());
            }

            long start = System.currentTimeMillis();
            String result = RedisUtils.execute(commandName, params, this.dbEntity.getUniqueId(), this.dbEntity.getDbIndex());
            ZslRedisDesktopMainWindow.getZslLogPanel().log("控制台执行["+ commandName +"]命令耗时：" + (System.currentTimeMillis() - start) + "毫秒！");

            if(!StringUtils.isEmpty(result)){
                int formattedDisplay = ZslRedisDesktopMainWindow.getOtherGeneralConfig().getFormattedDisplay();
                if(BooleanEnum.isTrue(formattedDisplay)){
                    this.append(JsonOutUtils.formatJson(result));
                }else{
                    this.append(result);
                }
            }

            this.append("\n");

            if(CmdCommandEnum.SELECT.getCommandName().equalsIgnoreCase(commandName) && Constants.OK.equalsIgnoreCase(result)){
                int dbIndex = Integer.parseInt(params.get(0));
                this.dbEntity.setDbIndex(dbIndex);
                this.prefix = this.buildPrefix(this.dbEntity);
            }

            this.append(this.prefix);
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int extendedKeyCode = e.getExtendedKeyCode();
        this.setCurrentKeyCode(extendedKeyCode);

        if(this.currentPosition < this.buffer.length() && !this.isOrientationKey(extendedKeyCode)){
            if(extendedKeyCode == KeyEvent.VK_ENTER){
                this.console.setCaretPosition(this.buffer.length());
            }
            e.consume();
        } else if(this.currentPosition == this.buffer.length() && extendedKeyCode == KeyEvent.VK_BACK_SPACE){
            e.consume();
        }else if(extendedKeyCode == KeyEvent.VK_SHIFT){
            e.consume();
        }else if(extendedKeyCode == KeyEvent.VK_UP){
            if(HISTORY_COMMAND_INDEX >= 1 && HISTORY_COMMAND_INDEX <= historyCommandList.size()){
                this.console.setText(this.buffer.toString());
                this.console.append(historyCommandList.get(--HISTORY_COMMAND_INDEX));
            }

            e.consume();
        }else if(extendedKeyCode == KeyEvent.VK_DOWN){
            if(HISTORY_COMMAND_INDEX >= -1 && HISTORY_COMMAND_INDEX < historyCommandList.size() - 1){
                this.console.setText(this.buffer.toString());
                this.console.append(historyCommandList.get(++HISTORY_COMMAND_INDEX));
            }
            e.consume();
        }

    }

    private boolean isOrientationKey(int keyCode){
        return KeyEvent.VK_LEFT == keyCode || KeyEvent.VK_UP == keyCode || KeyEvent.VK_RIGHT == keyCode || KeyEvent.VK_DOWN == keyCode;
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void caretUpdate(CaretEvent e) {
        this.currentPosition = e.getDot();

    }

    private void appendAsync(final String content){
        SwingUtilities.invokeLater(() ->this.append(content));
    }

    public synchronized void setCurrentKeyCode(int keyCode){
        this.currentKeyCode = keyCode;
    }

    public synchronized int getCurrentKeyCode(){
        return this.currentKeyCode;
    }

    public synchronized void append(String content){
        if(!Objects.isNull(content)){
            this.console.append(content);
            this.buffer.append(content);
        }
    }

    public synchronized void clear(){
        this.console.setText(this.prefix);
        this.buffer = new StringBuffer();
        this.buffer.append(this.prefix);
    }

    public static void showCmdWindow(NodeEntity dbEntity){
        new ZslRedisCmdWindows(dbEntity);
    }


}
